#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kallsyms.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/utsname.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/syscalls.h>

#define MAX_LOGCALLS 64
#define MAX_PATH_LEN 64

unsigned long *sys_call_tb; // = (void*)0xffffffff81804ec8;

asmlinkage long (*original_open)(char *, int, int); // new args here for open
asmlinkage long (*original_execve)(const char *filename, char *const argv[],char *const envp[]); // new args here for execv
asmlinkage long (*original_logcalls)(const char * path);

struct logcalls
{
	pid_t pid;
	char path[MAX_PATH_LEN];
} log_list[MAX_LOGCALLS];

int loggedIndex = 0;

int getListIndex(pid_t);

void new_itoa(int, char*, int);
char *sput_i(int, char *, int);
char *sput_ip1(int, char *, int);

void new_itoa(int integer, char *string, int base){
	if (0 > integer){
		++integer;
		*string++ = '-';
		*sput_ip1(-integer, string, base) = '\0';
	} else {
		*sput_i(integer, string, base) = '\0';
	}
}

char *sput_i(int integer, char *string, int bs){
	if (integer / bs != 0) {
		string = sput_i(integer / bs, string, bs);
	}
	*string++ = (char)('0' + integer % bs); 

	return string;
}

char *sput_ip1(int integer, char *string, int bs){
	int digit;

	digit = (integer % bs + 1) % bs;
	if (integer / bs != 0){
		string = (digit == 0 ? sput_ip1 : sput_i)(integer / bs, string, bs);
		*string++ = (char)('0' + digit);
	} else {
		if (digit == 0){
			*string++ = '1';
		}
		*string++ = (char)('0' + digit); 
	}
	return string;
}

static void write_file(int index, char * data){
	loff_t pos = 0;
	struct file *fd;

	char * path = log_list[index].path;

	mm_segment_t old_fs = get_fs();

	fd = filp_open(path, O_WRONLY | O_CREAT, 0644);

	if (IS_ERR(fd)){
		printk("%ld\n", PTR_ERR(fd));
		return;
	}

	if(fd >= 0){
		vfs_write(fd, data, strlen(data), &pos);
		//filep = fget(fd);

		//if(filep){	
		//	vfs_write(filep, data, strlen(data), &pos);
		//	fput(filep);
		//}

		filp_close(fd, NULL);
	}

	set_fs(old_fs);
}



asmlinkage long sys_logcalls(const char * path){
  pid_t pdiddy;
 printk(KERN_INFO "TEST it worked!!!\n");
	if (log_list != NULL) { return -1; }
 
	strcpy(log_list[loggedIndex].path, path);


	if ((pdiddy = current->pid) < 0)
	{
		//printk("SYS_getpid failed:  ", errno);
		pdiddy = -1;
	}

	log_list[loggedIndex++].pid = pdiddy;

	return 0;
}

/* http://memset.wordpress.com/2011/01/20/syscall-hijacking-dynamically-obtain-syscall-table-address-kernel-2-6-x/ */
asmlinkage long new_execve(const char *filename, char *const argv[], char *const envp[]){
	pid_t pdiddy;
   char current_pid[32];
    int ndxList = -1;
    
    	current_pid[31] = '\0';
       
	if ((pdiddy = current->pid) < 0)
	{
		//printk("SYS_getpid failed:  ", errno);
		pdiddy = -1;
	}
 // index
	if ((ndxList = getListIndex(pdiddy)) >= 0)
	{
   
   char fileBuf[96];
   int i = 0;
		// pdiddy = PID

		new_itoa(pdiddy, current_pid, 10);

		write_file(ndxList, current_pid);

		write_file(ndxList, " | EXECV | ");

		strcpy(fileBuf, filename);
		write_file(ndxList, fileBuf);

		write_file(ndxList, " | ");

                while (argv != NULL && argv[i] != NULL)
                {        
                        char argvBuf[64];
                        argvBuf[63] = '\0';
                        argvBuf[0] = '\0';
                        strcpy(argvBuf, argv[i++]);
                        write_file(ndxList, argvBuf);
                }

		write_file(ndxList, " | ");

		i = 0;
		while (envp != NULL && envp[i] != NULL)
		{
			char envpBuf[64];
			envpBuf[63] = '\0';
			envpBuf[0] = '\0';
                	strcpy(envpBuf, envp[i++]);
			write_file(ndxList, envpBuf);
		}

		write_file(ndxList, " \n ");
	}
   
   //kfree(current_pid);

	return (*original_execve)(filename, argv, envp);
}

asmlinkage long new_open(char * filename, int flags, int mode){
	pid_t pdiddy;
   int ndxList = -1;
   char current_pid[32];
   char current_flags[32];
   char current_mode[32];
   
   current_pid[31] = '\0';
   current_flags[31] = '\0';
   current_mode[31] = '\0';
	if ((pdiddy = current->pid) < 0)
	{
		//printk("SYS_getpid failed:  ", errno);
		pdiddy = -1;
	}
 // index
	if ((ndxList = getListIndex(pdiddy)) >= 0)
	{
		// pdiddy = PID
		new_itoa(pdiddy, current_pid, 10);
  
		write_file(ndxList, current_pid);

		write_file(ndxList, " | OPEN | ");

		write_file(ndxList, filename);

		write_file(ndxList, " | ");

    		new_itoa(flags, current_flags, 16);

		write_file(ndxList, current_flags);

		write_file(ndxList, " | ");

    		new_itoa(mode, current_mode, 8);

		write_file(ndxList, current_mode);

		write_file(ndxList, " \n ");
	}
   
    //kfree(current_pid);
    //kfree(current_flags);
    //kfree(current_mode);

	return (*original_open)(filename, flags, mode);
}

int getListIndex(pid_t curPID)
{
	if (curPID >= 0)
	{
	int x;
	// iterate through log_list... find AND return the index where           "curPID==log_list[x].pid"
	for(x=0; x < MAX_LOGCALLS; x++){
		if(curPID == log_list[x].pid){
			return x;
		}
	}
	}

	// error, could not find pid in log_list
	return -1;
}

static unsigned long **aquire_sys_call_tb(void)
{
	unsigned long int offset = PAGE_OFFSET;
	unsigned long **sct;

	while (offset < ULLONG_MAX) {
		sct = (unsigned long **)offset;

		if (sct[__NR_restart_syscall] == (unsigned long *) restart_syscall) 
			return sct;

		offset += sizeof(void *);
	}

	return NULL;
}

static void disable_page_protection(void) 
{
	unsigned long value;

write_cr0(read_cr0() & (~ 0x10000));

	asm volatile("mov %%cr0, %0" : "=r" (value));

	if(!(value & 0x00010000))
		return;

	asm volatile("mov %0, %%cr0" : : "r" (value & ~0x00010000));
}

static void enable_page_protection(void) 
{
	unsigned long value;
	asm volatile("mov %%cr0, %0" : "=r" (value));

	if((value & 0x00010000))
		return;

	asm volatile("mov %0, %%cr0" : : "r" (value | 0x00010000));

write_cr0(read_cr0() | 0x10000);
}

static int __init logcallsInit(void){
    sys_call_tb = get_sys_call_table(); //aquire_sys_call_tb();

//get_sys_call_table
printk(KERN_INFO "syscall table address:  %p\n", (void*)sys_call_tb);

printk(KERN_INFO "Initialized sys call!\n");

/*    disable_page_protection();

    original_execve = (void *)sys_call_tb[__NR_execve];
    original_open = (void *)sys_call_tb[__NR_open];

#define REPLACE(x) original_##x = sys_call_tb[__NR_##x];        \
        sys_call_tb[__NR_##x] = new_##x

	REPLACE(execve);
//    sys_call_tb[__NR_execve] = (unsigned long*)new_execve;

	REPLACE(open);
//    sys_call_tb[__NR_open] = (unsigned long*)new_open;

//	REPLACE(logcalls);
*/
    sys_call_tb[350] = (unsigned long*)sys_logcalls;
/*
#undef REPLACE

    enable_page_protection();
*/
    return 0;
}

static void __exit logcallsExit(void){
/*    disable_page_protection();

    sys_call_tb[__NR_execve] = (unsigned long *)original_execve;

    sys_call_tb[__NR_open] = (unsigned long *)original_open;

    enable_page_protection();
*/
    //kfree(log_list);
}

module_init(logcallsInit);
module_exit(logcallsExit);

MODULE_AUTHOR("TRENT APPLE, AARON PETRY");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("(¯`·._(¯`·._(¯`·._( ĻÖȞ )_.·´¯)_.·´¯)_.·´¯)");

